package com.chinaisafe.gateway.core.auth;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.chinaisafe.base.common.enums.AnswerCodeEnum;
import com.chinaisafe.base.common.vo.Answer;
import com.chinaisafe.gateway.sdk.constant.GateWayConstant;
import com.chinaisafe.gateway.sdk.vo.request.*;
import com.chinaisafe.gateway.sdk.vo.response.GatewayCheckUserRespVo;
import com.chinaisafe.gateway.sdk.vo.response.Oauth2ClientLoginRespVo;
import com.chinaisafe.gateway.sdk.vo.response.Oauth2UserLoginRespVo;
import com.chinaisafe.gateway.server.config.properties.ChinaisafeAuthorizationProperties;
import com.chinaisafe.gateway.server.service.RestTemplateService;
import com.chinaisafe.gateway.server.service.user.IGatewayUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Objects;

/**
 * TokenServiceImpl
 *
 * @author xuefu.shu
 * @date 2024-07-04 16:12
 */
@Slf4j
@Component
public class GatewayTokenServiceImpl implements IGatewayUserService {

    @Autowired
    private SaTokenDao saTokenDao;
    @Resource
    protected RestTemplateService restTemplateService;

    @Resource
    protected ChinaisafeAuthorizationProperties chinaisafeAuthorizationProperties;

    /**
     * 小时转秒
     */
    private static final Integer TOKEN_EXPIRE_TIME = 60 * 60;

    @Override
    public Answer<String> getToken(GateWayGetTokenReqVo tokenReqVo) {
        if (Objects.isNull(tokenReqVo.getAccountId())) {
            return Answer.fail("account id is not find");
        }

        Map<String, String> clients = chinaisafeAuthorizationProperties.getClients();
        if (CollectionUtil.isEmpty(clients) || Objects.isNull(tokenReqVo.getClientId()) || !clients.containsKey(tokenReqVo.getClientId()) ||
                !clients.get(tokenReqVo.getClientId()).equals(tokenReqVo.getClientSecret())) {
            return Answer.fail("The client authentication information is incorrect");
        }

/// 指定参数登录，后续使用时放出来修改
//        SaLoginModel loginModel = new SaLoginModel()
//                // 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型
//                .setDevice(tokenReqVo.getClientType().name())
//                // 是否为持久Cookie（临时Cookie在浏览器关闭时会自动删除，持久Cookie在重新打开后依然存在）
//                .setIsLastingCookie(false)
//                // 指定此次登录token的有效期, 单位:秒 （如未指定，自动取全局配置的 timeout 值）
//                .setTimeout(TOKEN_EXPIRE_TIME * tokenReqVo.getTimeout())
//                .setToken("xxxx-xxxx-xxxx-xxxx") // 预定此次登录生成的Token
//                .setExtra("name", "zhangsan")    // Token挂载的扩展参数 （此方法只有在集成jwt插件时才会生效）
//                .setIsWriteHeader(false)         // 是否在登录后将 Token 写入到响应头
//                .setActiveTimeout(300)           // 指定此次登录token的最低活跃频率, 单位:秒，设置此参数需要在配置文件打开dynamicActiveTimeout=true
//                .setTokenSignTag("xxx");// 指定此次登录挂载在 TokenSign 上的 tag 值, 任意值
//        StpUtil.login(tokenReqVo.getUserId(), loginModel);

        if (Objects.nonNull(tokenReqVo.getClientType())) {
            StpUtil.login(tokenReqVo.getAccountId(), tokenReqVo.getClientType().name());
        } else {
            StpUtil.login(tokenReqVo.getAccountId());
        }
        String tokenValue = StpUtil.getTokenValue();
        if (Objects.nonNull(tokenReqVo.getCacheUser())) {
            saTokenDao.set(StrUtil.format(GateWayConstant.TENANCY_REDIS_KEY, tokenValue), JSONUtil.toJsonStr(tokenReqVo.getCacheUser()), SaManager.getConfig().getTimeout());
        }

        return Answer.successWithData(tokenValue);
    }


    @Override
    public Answer<Boolean> loginOut(GateWayLoginOutReqVo tokenReqVo) {
        log.info("logoutByTokenValue:{}", tokenReqVo.getToken());
        if (StrUtil.isBlank(tokenReqVo.getToken())) {
            return Answer.fail("token is null");
        }

        Map<String, String> clients = chinaisafeAuthorizationProperties.getClients();
        if (CollectionUtil.isEmpty(clients) || Objects.isNull(tokenReqVo.getClientId()) || !clients.containsKey(tokenReqVo.getClientId()) ||
                !clients.get(tokenReqVo.getClientId()).equals(tokenReqVo.getClientSecret())) {
            return Answer.fail("The client authentication information is incorrect");
        }
        StpUtil.logoutByTokenValue(tokenReqVo.getToken());
        return Answer.successWithData(Boolean.TRUE);
    }

    @Override
    public Answer<Boolean> cacheUser(GatewayCacheUserReqVo tokenReqVo) {
        log.info("cacheUser:{}", tokenReqVo.getToken());
        if (StrUtil.isBlank(tokenReqVo.getToken())) {
            return Answer.fail("token is null");
        }

        Map<String, String> clients = chinaisafeAuthorizationProperties.getClients();
        if (CollectionUtil.isEmpty(clients) || Objects.isNull(tokenReqVo.getClientId()) || !clients.containsKey(tokenReqVo.getClientId()) ||
                !clients.get(tokenReqVo.getClientId()).equals(tokenReqVo.getClientSecret())) {
            return Answer.fail("The client authentication information is incorrect");
        }

        Object userId = StpUtil.getLoginIdByToken(tokenReqVo.getToken());
        if (Objects.isNull(userId)) {
            return Answer.fail("token is invalid");
        }

        saTokenDao.set(StrUtil.format(GateWayConstant.TENANCY_REDIS_KEY), JSONUtil.toJsonStr(tokenReqVo.getUser()), SaManager.getConfig().getTimeout());

        return Answer.success();
    }

    @Override
    public Answer<String> changeToken(GatewayChangeTokenReqVo tokenReqVo) {

        log.info("cacheUser:{}", tokenReqVo.getToken());
        if (StrUtil.isBlank(tokenReqVo.getToken())) {
            return Answer.fail("token is null");
        }

        Map<String, String> clients = chinaisafeAuthorizationProperties.getClients();
        if (CollectionUtil.isEmpty(clients) || Objects.isNull(tokenReqVo.getClientId()) || !clients.containsKey(tokenReqVo.getClientId()) ||
                !clients.get(tokenReqVo.getClientId()).equals(tokenReqVo.getClientSecret())) {
            return Answer.fail("The client authentication information is incorrect");
        }

        Object userId = StpUtil.getLoginIdByToken(tokenReqVo.getToken());
        if (Objects.isNull(userId)) {
            return Answer.fail("token is invalid");
        }

        if (Objects.nonNull(tokenReqVo.getUser()) && Objects.nonNull(tokenReqVo.getUser().getAccountId())) {
            StpUtil.login(tokenReqVo.getUser().getAccountId(), tokenReqVo.getClientType().name());
            String tokenValue = StpUtil.getTokenValue();
            saTokenDao.set(StrUtil.format(GateWayConstant.TENANCY_REDIS_KEY, tokenValue), JSONUtil.toJsonStr(tokenReqVo.getUser()), SaManager.getConfig().getTimeout());

            StpUtil.logoutByTokenValue(tokenReqVo.getToken());

            return Answer.successWithData(tokenValue);
        }
        return Answer.fail();
    }

    @Override
    public Answer<GatewayCheckUserRespVo> getUserInfo(GatewayCheckUserReqVo tokenReqVo) {
        try {
            String userServiceName = this.chinaisafeAuthorizationProperties.getUserServiceName();
            if (StrUtil.isBlank(userServiceName)) {
                if (log.isDebugEnabled()) {
                    log.debug("userServiceName is null,please check your config");
                }

                return null;
            } else {
                String userInfoUrl = this.chinaisafeAuthorizationProperties.getUserInfoUrl();
                if (StrUtil.isBlank(userInfoUrl)) {
                    if (log.isDebugEnabled()) {
                        log.debug("userInfoUrl is null,please check your config");
                    }

                    return null;
                } else {
                    ParameterizedTypeReference<Answer<GatewayCheckUserRespVo>> typeRef = new ParameterizedTypeReference<Answer<GatewayCheckUserRespVo>>() {
                    };
                    Answer<GatewayCheckUserRespVo> answer = this.restTemplateService.postEntity(userServiceName, userInfoUrl, tokenReqVo, typeRef);
                    if (Objects.isNull(answer)) {
                        if (log.isWarnEnabled()) {
                            log.warn("http check user response is null");
                        }

                        return Answer.create(AnswerCodeEnum.MAINTAIN, AnswerCodeEnum.MAINTAIN.getMessage(), null);
                    } else {
                        return answer;
                    }
                }
            }
        } catch (Exception var8) {
            log.error("http token check err msg:{}", var8.getMessage());
            return Answer.create(AnswerCodeEnum.MAINTAIN, AnswerCodeEnum.MAINTAIN.getMessage(), null);
        }
    }

    @Override
    public Answer<Oauth2UserLoginRespVo> getOauth2LoginUserInfo(Oauth2UserLoginReqVo user) {
        try {
            String userServiceName = this.chinaisafeAuthorizationProperties.getUserServiceName();
            if (StrUtil.isBlank(userServiceName)) {
                if (log.isDebugEnabled()) {
                    log.debug("userServiceName is null,please check your config");
                }

                return null;
            } else {
                String oauth2UserInfoUrl = this.chinaisafeAuthorizationProperties.getOauth2UserInfoUrl();
                if (StrUtil.isBlank(oauth2UserInfoUrl)) {
                    if (log.isDebugEnabled()) {
                        log.debug("oauth2UserInfoUrl is null,please check your config");
                    }

                    return null;
                } else {
                    ParameterizedTypeReference<Answer<Oauth2UserLoginRespVo>> typeRef = new ParameterizedTypeReference<Answer<Oauth2UserLoginRespVo>>() {
                    };
                    Answer<Oauth2UserLoginRespVo> answer = this.restTemplateService.postEntity(userServiceName, oauth2UserInfoUrl, user, typeRef);
                    if (Objects.isNull(answer)) {
                        if (log.isWarnEnabled()) {
                            log.warn("http user info response is null");
                        }

                        return Answer.create(AnswerCodeEnum.MAINTAIN, AnswerCodeEnum.MAINTAIN.getMessage(), null);
                    } else {
                        return answer;
                    }
                }
            }
        } catch (Exception var8) {
            log.error("http token check err msg:{}", var8.getMessage());
            return Answer.create(AnswerCodeEnum.MAINTAIN, AnswerCodeEnum.MAINTAIN.getMessage(), null);
        }
    }

    @Override
    public Answer<Oauth2ClientLoginRespVo> getOauth2LoginClientInfo(Oauth2ClientLoginReqVo client) {
        try {
            String userServiceName = this.chinaisafeAuthorizationProperties.getUserServiceName();
            if (StrUtil.isBlank(userServiceName)) {
                if (log.isDebugEnabled()) {
                    log.debug("userServiceName is null,please check your config");
                }

                return null;
            } else {
                String oauth2ClientInfoUrl = this.chinaisafeAuthorizationProperties.getOauth2ClientInfoUrl();
                if (StrUtil.isBlank(oauth2ClientInfoUrl)) {
                    if (log.isDebugEnabled()) {
                        log.debug("oauth2ClientInfoUrl is null,please check your config");
                    }

                    return null;
                } else {
                    ParameterizedTypeReference<Answer<Oauth2ClientLoginRespVo>> typeRef = new ParameterizedTypeReference<Answer<Oauth2ClientLoginRespVo>>() {
                    };
                    Answer<Oauth2ClientLoginRespVo> answer = this.restTemplateService.postEntity(userServiceName, oauth2ClientInfoUrl, client, typeRef);
                    if (Objects.isNull(answer)) {
                        if (log.isWarnEnabled()) {
                            log.warn("http client info response is null");
                        }

                        return Answer.create(AnswerCodeEnum.MAINTAIN, AnswerCodeEnum.MAINTAIN.getMessage(), null);
                    } else {
                        return answer;
                    }
                }
            }
        } catch (Exception var8) {
            log.error("http token check err msg:{}", var8.getMessage());
            return Answer.create(AnswerCodeEnum.MAINTAIN, AnswerCodeEnum.MAINTAIN.getMessage(), null);
        }
    }
}